package com.kevin.epacms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kevin.epacms.constant.CommonConstant;
import com.kevin.epacms.entity.User;
import com.kevin.epacms.entity.dto.LoginParam;
import com.kevin.epacms.entity.dto.RegisterParam;
import com.kevin.epacms.entity.dto.UserDto;
import com.kevin.epacms.entity.vo.ConditionVo;
import com.kevin.epacms.mapper.UserMapper;
import com.kevin.epacms.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kevin.epacms.util.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author kevin
 * @since 2022-11-10
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private RedisUtil redisUtil;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Override
    public User getUserByUsername(String username) {
        return lambdaQuery().eq(User::getUsername, username).one();
    }

    @Override
    public UserDto getCurrentUserByUsername(String username) {
        User user = lambdaQuery().eq(User::getUsername, username).one();
        return BeanUtil.toBean(user, UserDto.class);
    }

    @Override
    public Result changePassword(String password) {
        User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (passwordEncoder.matches(password, currentUser.getPassword())) {
            return Result.fail("不能与原密码一样");
        }
        String newPassword = passwordEncoder.encode(password);
        currentUser.setPassword(newPassword);
        return updateUser(currentUser);
    }

    @Override
    public Result resetPassword(Long uid) {
        User user = lambdaQuery().eq(User::getId, uid).one();
        String resetPwd = passwordEncoder.encode("666666");
        user.setPassword(resetPwd);
        return updateById(user) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result isExistUser(String username) {
        boolean exists = baseMapper.exists(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        return exists ? Result.fail("用户名已被注册") : Result.ok(CommonConstant.SUCCESS);
    }

    @Override
    public Result updateUser(User user) {
        return updateById(user) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result deleteUsers(String userIdsStr) {
        if (StrUtil.isBlank(userIdsStr)) {
            return Result.fail("请选择要删除的用户id");
        }
        String[] idsStr = userIdsStr.split(StrUtil.COMMA);
        List<Long> ids = Arrays.stream(idsStr).map(Long::parseLong).collect(Collectors.toList());
        return removeByIds(ids) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result getUsersByPage(Integer currentPage, Integer pageSize, List<ConditionVo> conditions) {
        QueryWrapper<User> queryWrapper = SearchUtil.parseWhereSql(conditions);
        Page<User> page = baseMapper.selectPage(new Page<>( currentPage, pageSize), queryWrapper);
        PageList userPageList = new PageList(page);
        userPageList.setDataList(BeanUtil.copyToList(userPageList.getDataList(), UserDto.class));
        return Result.ok(userPageList);
    }

    @Override
    public void export(HttpServletResponse response) {
        try {
            List<User> userList = lambdaQuery().list();
            ExcelWriter writer = ExcelUtil.getWriter(true);
            writer.addHeaderAlias("id", "用户ID");
            writer.addHeaderAlias("sno", "学号");
            writer.addHeaderAlias("username", "用户名");
            writer.addHeaderAlias("sex", "性别");
            writer.addHeaderAlias("realName", "真实姓名");
            writer.addHeaderAlias("email", "邮箱");
            writer.addHeaderAlias("phone", "电话");
            writer.addHeaderAlias("address", "地址");
            //一次性把list内的对象写入到excel，使用默认样式，强制输出标题
            writer.write(userList, true);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf8");
            String fileName = URLEncoder.encode("用户信息", "utf8");
            response.setHeader("Content-Disposition", "attachment:filename=" + fileName + ".xlsx");
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out, true);
            writer.close();
        } catch (IOException e) {
            log.error("用户信息导出异常: ", e);
        }
    }

    @Override
    public boolean isExistEmail(String email) {
        return lambdaQuery().eq(User::getEmail, email).exists();
    }

    @Override
    public Result deleteUserById(Long id) {
        return removeById(id) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result login(LoginParam loginParam, HttpServletRequest request) {
        String token;
        try {
            String username = loginParam.getUsername();
            String password = loginParam.getPassword();
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (Objects.isNull(userDetails) || !passwordEncoder.matches(password, userDetails.getPassword())) {
                return Result.fail("密码不正确");
            }

            // 更新登录用户对象
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            // 生成token
            token = jwtTokenUtil.generateToken(userDetails);
            Map<String, Object> tokenMap = new HashMap<>(3);
            tokenMap.put("token", token);
            tokenMap.put("tokenHead", tokenHead);
            return Result.ok("登录成功", tokenMap);
        }catch (AuthenticationException e){
            log.error("认证异常", e);
            return Result.fail(e.getMessage());
        }
    }

    @Override
    public Result register(RegisterParam registerParam) {
        if (!StrUtil.equals(registerParam.getPassword(), registerParam.getRepassword())) {
            return Result.fail(HttpStatus.BAD_REQUEST.value(), "两次密码不一致");
        }

        String key = CommonConstant.EMAIL_PREFIX + registerParam.getEmail();
        String code = redisUtil.get(key);
        if (!StrUtil.equals(code, registerParam.getCode())) {
            return Result.fail(HttpStatus.BAD_REQUEST.value(),"验证码错误");
        }

        boolean existEmail = isExistEmail(registerParam.getEmail());
        if(existEmail){
            return Result.fail(HttpStatus.BAD_REQUEST.value(), "邮箱已存在");
        }
        boolean exists = baseMapper.exists(new LambdaQueryWrapper<User>().eq(User::getUsername, registerParam.getUsername()));
        if(exists){
            return Result.fail("用户名已存在");
        }
        User user = new User();
        user.setUsername(registerParam.getUsername());
        user.setEmail(registerParam.getEmail());
        String password = passwordEncoder.encode(registerParam.getPassword());
        user.setPassword(password);
        user.setRegisterTime(LocalDateTime.now());
        return save(user) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }
}
